

#include "../tcp.h"
#include "../cmap.h"
#include "../uv_common.h"
#include "../uv_thread.h"
#include <errno.h>
#include <net/if.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>

static int g_epollfd = -1;
static CMAP* s_watchfds;

static void doShutdown(tcp_t* h)
{
    int ret = shutdown(h->socket, SHUT_RDWR);
    if (h->shutdown_cb) {
        h->shutdown_cb(h->shutdown_req, ret);
    }
}

static void doClose(tcp_t* h)
{
    int ret = close(h->socket);
    if (h->close_cb) {
        h->close_cb(h->shutdown_req, ret);
    }
}

static void removeFromEpoll(tcp_t* h)
{
    if (epoll_ctl(g_epollfd, EPOLL_CTL_DEL, h->socket, NULL) < 0) {
        fprintf(stderr, "epoll EPOLL_CTL_DEL error:%s\n", strerror(errno));
        abort();
    } else {
        printf("%s: remove from epoll success\n", __func__);
    }
}

static void* UWorkerThread(void* parm)
{
    int epollfd = *(int*)parm;
    static const int max_safe_timeout = 1789569;
    struct epoll_event events[1024];

    while (1) {
        int ret = epoll_wait(epollfd, events, ARRAY_SIZE(events), max_safe_timeout);

        if (ret > 0) {
            for (int i = 0; i < ret; i++) {

                /*
                    epollin
                        1. accept
                        2. recv
                    epollout
                        send
                    shutdown
                    close
                */

                printf("%x %p\n", events[i].events, events[i].data.ptr);
                tcp_t* h = (tcp_t*)events[i].data.ptr;
                removeFromEpoll(h);

                void* v = cmap_get(s_watchfds, &h, sizeof(h));
                if (v) {
                    if (EPOLLIN & events[i].events) { //IN
                        if (h->listen_req) {
                            if (h->on_connect_cb) {
                                h->on_connect_cb(h, 0);
                            }
                        } else if (h->recv_req) {
                            uv_buf_t buf = { RECVBUFLEN, (char*)malloc(RECVBUFLEN) };
                            ssize_t nread = recv(h->socket, buf.base, RECVBUFLEN, 0);
                            printf("%s:nread = %zd\n", __func__, nread);
                            if (h->recv_cb) {
                                h->recv_cb(h->recv_req, nread, &buf, NULL, ret);
                            } else {
                                free(buf.base);
                            }
                        }
                    } else if (EPOLLOUT & events[i].events) { //TODO OUT
                        int opt_val = 0;
                        if (EPOLLERR & events[i].events) {
                            socklen_t opt_len = sizeof(opt_val);
                            int ret = getsockopt(h->socket, SOL_SOCKET, SO_ERROR, &opt_val, &opt_len);
                            printf("%s : EPOLLOUT error %s \n", __func__, strerror(errno));
                        } else {
                            // socklen_t opt_len = sizeof(opt_val);
                            // int ret = getsockopt(h->socket, SOL_SOCKET, SO_ERROR, &opt_val, &opt_len);
                            printf("%s : EPOLLOUT\n", __func__);
                        }
                        if (h->on_connect_cb) { //连接req
                            h->on_connect_cb(h, opt_val);
                            h->on_connect_cb = NULL;
                        }
                        if(h->send_req){
                            tcp_send(h->send_req, h, h->send_req->buf.base, h->send_req->buf.len, h->send_cb);
                        }
                    } else if (EPOLLERR & events[i].events) {
                        printf("%s : EPOLLERR\n", __func__);
                    } else {
                        printf("%s : unhanled \n", __func__);
                    }

                    if (h->shutdown_req) {
                        doShutdown(h);
                    }
                    if (h->close_req) {
                        doClose(h);
                    }
                } else {
                    printf("%s have removed from watch map!!\n", __func__);
                }
            }
        } else {
            printf("%s:%d %s\n", __func__, ret, strerror(errno));
        }
    }

    return NULL;
}

int tcp_init()
{
    hookSignal();
    if (g_epollfd == -1) {
        if ((g_epollfd = epoll_create(0x7fffffff)) == -1) {
            return errno;
        }
        s_watchfds = cmap_new();
        uv_thread_t tid;
        uv_thread_create(&tid, (uv_thread_cb)UWorkerThread, &g_epollfd);
    }
    return 0;
}

int tcp_deletehandler(tcp_t* tcph)
{
    int ret = 0;
    if (tcph) {
        if (tcph->addr) {
            free(tcph->addr);
            tcph->addr = NULL;
        }
        if (tcph->listen_req) {
            free(tcph->listen_req);
            tcph->listen_req = NULL;
        }
        if (tcph->recv_req) {
            free(tcph->recv_req);
            tcph->recv_req = NULL;
        }
    }

    return ret;
}

int tcp_createhandler(tcp_t* tcphandle, struct sockaddr* addr, size_t addrlen)
{
    int ret = 0;
    //tcphandle = (tcp_t*)calloc(1, sizeof(tcp_t));
    memset(tcphandle, 0, sizeof(tcp_t));
    if ((tcphandle->socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        return errno;
    }

    cmap_put2(s_watchfds, &tcphandle, sizeof(tcphandle), (void*)1, NULL);
    printf("%s cmap put %p\n", __func__, tcphandle);

    //set reuse;
    int opt = SO_REUSEADDR; //TBD
    setsockopt(tcphandle->socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    if (fcntl(tcphandle->socket, F_SETFL, fcntl(tcphandle->socket, F_GETFD, 0) | O_NONBLOCK) == -1) {
        printf("%s set noblock failed %d\n", __func__, errno);
        return errno;
    }

    if (addr == NULL || addrlen <= 0) {
        // tcphandle->addrlen = sizeof(struct sockaddr_in);
        // tcphandle->addr = malloc(tcphandle->addrlen);
        // uv_ip4_addr("0.0.0.0", 0, (struct sockaddr_in*)tcphandle->addr);
    } else {
        tcphandle->addr = malloc(addrlen);
        tcphandle->addrlen = addrlen;
        memcpy(tcphandle->addr, addr, addrlen);

        if (bind(tcphandle->socket, (struct sockaddr*)tcphandle->addr, addrlen) == -1) {
            ret = errno;
            printf("bind failed %d\n", ret);
            return ret;
        }
    }

    ret = tcp_getsockname(tcphandle, tcphandle->addr, &tcphandle->addrlen);

    return ret;
}

void tcp_recv_start(tcp_t* tcphandle, uv_recv_cb cb)
{
    struct epoll_event ev;
    ev.events = EPOLLIN;

    tcphandle->recv_req = (RecvReq_t*)malloc(sizeof(RecvReq_t));
    if (tcphandle->recv_req) {
        tcphandle->recv_req->handle = tcphandle;
    }
    tcphandle->recv_cb = cb;

    ev.data.ptr = tcphandle;
    printf("%s %p\n", __func__, ev.data.ptr);

    if (epoll_ctl(g_epollfd, EPOLL_CTL_ADD, tcphandle->socket, &ev) < 0) {
        fprintf(stderr, "%s epoll set insertion error:%s\n",__func__, strerror(errno));
        abort();
    } else {
        printf("%s:add socket to epoll success\n", __func__);
    }
}

int tcp_listen(tcp_t* tcphandle, int backlog, uv_connection_cb cb)
{
    int ret = 0;
    struct epoll_event ev;
    ev.events = EPOLLIN;

    tcphandle->listen_req = (ReqCommon_t*)malloc(sizeof(ReqCommon_t));
    if (tcphandle->listen_req) {
        tcphandle->listen_req->handle = tcphandle;
    }
    tcphandle->on_connect_cb = cb;

    ev.data.ptr = tcphandle;

    if ((ret = listen(tcphandle->socket, backlog))) {
        printf("%s listen failed %s\n", __func__, strerror(errno));
        abort();
    }

    if (epoll_ctl(g_epollfd, EPOLL_CTL_ADD, tcphandle->socket, &ev) < 0) {
        fprintf(stderr, "%d epoll set insertion error:%s\n", tcphandle->socket, strerror(errno));
        abort();
    } else {
        printf("%s:add tcp_listen to epoll success\n", __func__);
    }

    return ret;
}

int tcp_accept(tcp_t* server, tcp_t* client)
{
    if (!client) {
        return -1;
    }

    client->addrlen = sizeof(struct sockaddr_in);
    struct sockaddr_in* addr = malloc(client->addrlen);
    if ((client->socket = accept(server->socket, (struct sockaddr*)addr, (socklen_t*)&client->addrlen)) < 0) {
        printf("%s accept failed %s\n", __func__, strerror(errno));
    }
    client->addr = (struct sockaddr*)addr;
    return 0;
}

static void AddSendReq(tcp_t* tcphandle, SendReq_t* req)
{

    tcphandle->send_req = req;
    struct epoll_event ev;
    ev.events = EPOLLOUT|EPOLLERR;
    ev.data.ptr = tcphandle;

    if (epoll_ctl(g_epollfd, EPOLL_CTL_ADD, tcphandle->socket, &ev) < 0) {
        fprintf(stderr, "%s:%d epoll set insertion error:%s\n", __func__, tcphandle->socket, strerror(errno));
        abort();
    } else {
        printf("%s:AddSendReq to epoll success\n", __func__);
    }
}

void tcp_send(SendReq_t* req, tcp_t* tcphandle, const char* buf, size_t buflen, uv_send_cb cb)
{
    int ret = 0;
    req->handle = tcphandle;

    {
        while (buflen > 0) {
            ret = send(tcphandle->socket, buf, buflen, 0);
            if (ret == -1) {
                if (ret == EINTR) {
                    continue;
                } else {
                    break;
                }
            } else {
                buflen -= ret;
                buf += ret;
            }
        }
    }

    if (buflen == 0 && cb) { //send success
        cb(req, 0);
    } else {
        if (errno == EAGAIN) { //异步发送剩余数据
            req->buf.base = buf;
            req->buf.len = buflen;
            AddSendReq(tcphandle, req);
            tcphandle->send_cb = cb;
            sleep(1);
        } else { // error happened
            cb(req, errno);
        }
    }
    printf("%s send left len = %zu\n", __func__, buflen);
}

void tcp_connect(tcp_t* tcphandle, const struct sockaddr* addr, size_t addrlen, uv_connection_cb cb)
{
    int ret = 0;
    ret = connect(tcphandle->socket, addr, addrlen);
    if (ret == -1) {
        if (errno == EINPROGRESS) {
            struct epoll_event ev;
            ev.events = EPOLLOUT;
            ev.data.ptr = tcphandle;
            tcphandle->on_connect_cb = cb;
            if (epoll_ctl(g_epollfd, EPOLL_CTL_ADD, tcphandle->socket, &ev) < 0) {
                fprintf(stderr, "%d epoll set insertion error:%s\n", tcphandle->socket, strerror(errno));
                abort();
            } else {
                printf("%s:add tcp_connect to epoll success\n", __func__);
            }
            return;
        }
    }

    if (cb) {
        cb(tcphandle, ret);
    }
}

void tcp_shutdown(ReqCommon_t* req, tcp_t* tcphandle, uv_shutdown_cb cb)
{
    req->handle = tcphandle;
    tcphandle->shutdown_req = req;
    tcphandle->shutdown_cb = cb;
}

void tcp_close(ReqCommon_t* req, tcp_t* tcphandle, uv_close_cb cb)
{
    req->handle = tcphandle;
    tcphandle->close_req = req;
    tcphandle->close_cb = cb;
}

int tcp_getsockname(const tcp_t* handle, struct sockaddr* name, int* namelen)
{
    if (handle->socket == -1) {
        return EINVAL;
    }

    if (getsockname(handle->socket, name, (socklen_t*)namelen) != 0) {
        return errno;
    }

    return 0;
}
